package grammatica;

import java.util.ArrayList;

public class Grammatica {

	private ArrayList<Rule> rules;

	public Grammatica() {
		rules = new ArrayList<Rule>();
	}

	public Rule addDefinition( String nonTerminal ) {
		for(Rule r : rules){
			if(r.getDefinitionName().trim().equals(nonTerminal.trim()))
				return r;
		}
		Rule r = new Rule(nonTerminal.trim());
		rules.add(r);
		return r;		
	}
	
	public void printAlles(){
		for(Rule d: rules){			
			System.out.println(d.getDefinitionName() +" ::= " + d.printDefinitions());
			
		}
	}
	
	public void check(){
		System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
		System.out.println("+                                  CHECKING                                    +");
		System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
		System.out.println("Checking definitions...");
		System.out.println("Checking definitions:  " + checkUndefinedDefinitions());
		System.out.println();
		System.out.println("Checking multiplicity in starters...");
		System.out.println("Checking multiplicity in starters  " + checkRule1());
		System.out.println();
		System.out.println("Checking empty non-terminals for identical starters and followers...");
		System.out.println("Checking empty non-terminals for identical starters and followers  " + checkRule2());
		System.out.println();
		System.out.println("Checking for recursion errors...");
		System.out.println("Checking for recursion errors.  " + checkRule3());
		System.out.println();
		System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
		
	}
	
	private String checkRule1() {
		boolean failed = false;
		for(Rule r :rules){
			ArrayList<Starter> starters = r.getStarters(this);
			failed = r.checkStarterMultiplicity(starters, true);
		}
		if(failed) return "FAILED";
		else return "PASSED";
	}
	
	private String checkRule2(){
		boolean failed = false;
		for( Rule r: rules ){
			if( r.canBeEmpty() ){
				ArrayList<Starter> starters = r.getStarters(this);
				ArrayList<Follower> followers = r.getFollowers(this);
				for(Starter s: starters){
					for(Follower f: followers){
						if(s.compareSet(f.getSet() ) ){
							System.out.println("failed check in definition of: " + r.getDefinitionName());
							System.out.println("Both starter and follower defined as: " + s);
							failed = true;							
						}
					}
				}
			}
		}	
		if(failed) return "FAILED";
		else return "PASSED";
	}

	private String checkRule3() {
		boolean failed = false;
		for (Rule r : rules) {
			NonTerminal n = r.getIdentifier();
			boolean fail = checkFirstNonTerminal(r, n);
			if(fail)System.out.println("recursion error in " + n.name);
			failed = failed || fail;
		}
		if(failed) return "FAILED";
		else return "PASSED";
	}
	
	public Rule getMatchingRule(NonTerminal nt){
		for(Rule r : rules)if(nt.equals(r.getIdentifier()))return r;
		return null;
	}

	private String checkUndefinedDefinitions() {
		ArrayList<NonTerminal> definedNonTerminals = new ArrayList<NonTerminal>();
		for(Rule r : rules){
			definedNonTerminals.add(r.getIdentifier());
		}
		boolean errors = false;
		for(Rule r : rules){
			errors = r.checkNonTerminals(definedNonTerminals) || errors;			
		}
		if(errors) return "FAILED";
		else return "PASSED";
	}
	
	

	public boolean checkFirstNonTerminal(Rule r, NonTerminal n) {
		for (Definition d : r.getDefinitions()) {
			Term t = d.get(0);
			if (t instanceof NonTerminal) {
				if ( t.equals(n) ) {
					System.out.println(t.getName() + " " + n.getName() );
					return true;
				}
				Rule rule = getMatchingRule((NonTerminal) t);
				if(checkFirstNonTerminal(rule, n))return true;
			}
		}
		return false;
	}

	public ArrayList<Rule> getRules() {
		return rules;
	}
}
